りおんクロニクル


SQLite × Repository + Unit of Work|将来のDB移行に強いアーキテクチャ【2026年版】

Home【2026年版】C# / .NET入門と実践ガイド|基礎・業務アプリ開発・SQLite連携まで体系的に解説

SQLiteは軽量で扱いやすい一方、アプリが成長するとDBアクセスが散らばり、保守が難しくなるという問題があります。 そこで役立つのが Repositoryパターン + Unit of Work(UoW)。 この2つを組み合わせることで、DBアクセスの一元化・テスト容易性・将来のDB移行が圧倒的に楽になります。

この記事でわかること
・Repositoryパターンの役割
・Unit of Workでトランザクションを統合する方法
・SQLite × C# の実装例(非同期対応)
・将来のDB移行に強いアーキテクチャ
・業務アプリ向けベストプラクティス

1. Repositoryパターンとは?

Repositoryは、DBアクセスを抽象化する層です。 UIやViewModelはDBの種類を意識せず、Repositoryを通してデータを扱います。

■ 役割

2. Unit of Work(UoW)とは?

Unit of Workは、複数Repositoryの操作を1つのトランザクションにまとめる仕組みです。

■ 役割

重要: SQLiteは同時書き込みに弱いため、UoWで書き込みを1つにまとめるのは非常に効果的。

3. サンプルドメイン(User + Order)

今回は以下の2テーブルを例にします。

Users(Id, Name)
Orders(Id, UserId, Amount)

4. Repositoryインターフェース

public interface IUserRepository
{
    Task<User?> GetByIdAsync(int id);
    Task<IEnumerable<User>> GetAllAsync();
    Task AddAsync(User user);
    Task UpdateAsync(User user);
    Task DeleteAsync(int id);
}

public interface IOrderRepository
{
    Task<IEnumerable<Order>> GetByUserIdAsync(int userId);
    Task AddAsync(Order order);
}

5. Unit of Workインターフェース

public interface IUnitOfWork : IAsyncDisposable
{
    IUserRepository Users { get; }
    IOrderRepository Orders { get; }

    Task CommitAsync();
    Task RollbackAsync();
}

6. SQLite実装(Microsoft.Data.Sqlite)

■ Unit of Work実装

using Microsoft.Data.Sqlite;

public class SqliteUnitOfWork : IUnitOfWork
{
    private readonly SqliteConnection _con;
    private readonly SqliteTransaction _tran;

    public IUserRepository Users { get; }
    public IOrderRepository Orders { get; }

    public SqliteUnitOfWork(string cs)
    {
        _con = new SqliteConnection(cs);
        _con.Open();

        _tran = _con.BeginTransaction();

        Users = new SqliteUserRepository(_con, _tran);
        Orders = new SqliteOrderRepository(_con, _tran);
    }

    public async Task CommitAsync() => await _tran.CommitAsync();
    public async Task RollbackAsync() => await _tran.RollbackAsync();

    public async ValueTask DisposeAsync()
    {
        await _tran.DisposeAsync();
        await _con.DisposeAsync();
    }
}

■ UserRepository実装

public class SqliteUserRepository : IUserRepository
{
    private readonly SqliteConnection _con;
    private readonly SqliteTransaction _tran;

    public SqliteUserRepository(SqliteConnection con, SqliteTransaction tran)
    {
        _con = con;
        _tran = tran;
    }

    public async Task<User?> GetByIdAsync(int id)
    {
        var cmd = _con.CreateCommand();
        cmd.Transaction = _tran;
        cmd.CommandText = "SELECT Id, Name FROM Users WHERE Id = @id";
        cmd.Parameters.AddWithValue("@id", id);

        using var reader = await cmd.ExecuteReaderAsync();
        if (!await reader.ReadAsync()) return null;

        return new User
        {
            Id = reader.GetInt32(0),
            Name = reader.GetString(1)
        };
    }

    public async Task AddAsync(User user)
    {
        var cmd = _con.CreateCommand();
        cmd.Transaction = _tran;
        cmd.CommandText = "INSERT INTO Users (Name) VALUES (@name)";
        cmd.Parameters.AddWithValue("@name", user.Name);
        await cmd.ExecuteNonQueryAsync();
    }

    public async Task UpdateAsync(User user)
    {
        var cmd = _con.CreateCommand();
        cmd.Transaction = _tran;
        cmd.CommandText = "UPDATE Users SET Name = @name WHERE Id = @id";
        cmd.Parameters.AddWithValue("@name", user.Name);
        cmd.Parameters.AddWithValue("@id", user.Id);
        await cmd.ExecuteNonQueryAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var cmd = _con.CreateCommand();
        cmd.Transaction = _tran;
        cmd.CommandText = "DELETE FROM Users WHERE Id = @id";
        cmd.Parameters.AddWithValue("@id", id);
        await cmd.ExecuteNonQueryAsync();
    }
}

7. Unit of Workの利用例(アプリ側)

public async Task CreateUserWithOrder()
{
    await using var uow = new SqliteUnitOfWork("Data Source=app.db");

    var user = new User { Name = "Taro" };
    await uow.Users.AddAsync(user);

    var order = new Order { UserId = user.Id, Amount = 5000 };
    await uow.Orders.AddAsync(order);

    await uow.CommitAsync(); // 2つのINSERTが1トランザクションで確定
}

これにより、複数テーブル更新が一貫性を持って処理されます。

8. 将来のDB移行が圧倒的に楽になる理由

Repository + UoW を導入すると、 UI層・ViewModel・サービス層はDBを意識しなくなるため、 SQLite → SQL Server / PostgreSQL への移行が非常に簡単になります。

■ 移行時に変えるのはここだけ

アプリ本体は一切変更不要。

9. 業務アプリ向けベストプラクティス

まとめ:Repository + UoW は“長期運用アプリの必須アーキテクチャ”

SQLite × C# の業務アプリを長く運用するなら、 Repository + Unit of Work は必須級のアーキテクチャです。 この記事をベースに、自分のプロジェクトに合わせて拡張してみてください。

前のページ  次のページ